namespace wToggle.Platform
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net.Http;
    using System.Threading.Tasks;

    using wToggle.Platform.Data;
    using wToggle.Platform.Interfaces;

    using XPlatUtils;

    public class ClientProvider : IClientProvider
    {
        public async Task<ICollection<Client>> GetClients()
        {
            var cached = CacheLayer.LoadEntities<Client>();

            if (cached != null)
            {
                return cached.Any() ? cached : null;
            }

            var auth = ServiceContainer.Resolve<ITogglAuth>();

            using (var client = auth.GetPreauthenticatedClient())
            {
                var clients = await client.GetJsonAsync<List<Client>>("clients") ?? new List<Client>();

                await CacheLayer.StoreEntities(clients);

                return clients.OrderBy(c => c.Name).ToList();
            }
        }

        public async Task<ICollection<Client>> GetClients(ulong workspaceId)
        {
            if (workspaceId <= 0)
            {
                throw new ArgumentOutOfRangeException("workspaceId", "Invalid workspace ID specified");
            }

            var cached = CacheLayer.LoadEntities<Client>();

            if (cached != null)
            {
                var clients = cached.Where(c => c.WorkspaceId == workspaceId).ToList();
                if (clients.Any())
                {
                    return clients;
                }
            }

            var auth = ServiceContainer.Resolve<ITogglAuth>();

            using (var client = auth.GetPreauthenticatedClient())
            {
                var clients = await client.GetJsonAsync<List<Client>>("workspaces/" + workspaceId + "/clients") ?? new List<Client>();

                await CacheLayer.StoreEntities(clients);

                return clients.OrderBy(c => c.Name).ToList();
            }
        }

        public async Task<Client> GetClient(ulong id)
        {
            if (id <= 0)
            {
                throw new ArgumentException("Time client ID out of range");
            }

            var cached = CacheLayer.LoadEntity<Client>(id);

            if (cached != null)
            {
                return cached;
            }

            var auth = ServiceContainer.Resolve<ITogglAuth>();

            using (var client = auth.GetPreauthenticatedClient())
            {
                var result = await client.GetJsonAsync<ResponseData<Client>>("clients/" + id);

                if (result == null || result.Data == null)
                {
                    return null;
                }

                await CacheLayer.StoreEntity(result.Data);

                return result.Data;
            }
        }

        public async Task<Client> Update(Client client, bool withLock = true)
        {
            if (client == null)
            {
                throw new ArgumentNullException("client");
            }

            if (client.Id <= 0)
            {
                throw new ArgumentException("Tag ID out of range");
            }

            if (Toggly.HasInternetAccess())
            {
                var auth = ServiceContainer.Resolve<ITogglAuth>();

                using (var c = auth.GetPreauthenticatedClient())
                {
                    var result =
                        await
                        c.PutJsonAsync<ResponseData<Client>, ClientRequest>(
                            "clients/" + client.Id,
                            new ClientRequest { Data = client });

                    if (result == null)
                    {
                        return null;
                    }

                    await CacheLayer.StoreEntity(result.Data, withLock);

                    return result.Data;
                }
            }
            else
            {
                await CacheLayer.StoreEntryWrite(client, WriteType.Update, withLock);
                return client;
            }
        }

        public async Task<Client> Create(Client entry, bool withLock = true)
        {
            if (entry == null)
            {
                throw new ArgumentNullException("entry");
            }

            if (Toggly.HasInternetAccess())
            {
                var auth = ServiceContainer.Resolve<ITogglAuth>();

                using (var client = auth.GetPreauthenticatedClient())
                {
                    var result =
                        await
                        client.PostJsonAsync<ResponseData<Client>, ClientRequest>(
                            "clients",
                            new ClientRequest { Data = entry });

                    if (result == null)
                    {
                        return null;
                    }

                    entry.Id = result.Data.Id;
                    await CacheLayer.StoreEntity(result.Data, withLock);

                    return result.Data;
                }
            }
            else
            {
                await CacheLayer.StoreEntryWrite(entry, WriteType.Create, withLock);
                return entry;
            }
        }

        public async Task<bool> Delete(ulong id, bool withLock = true)
        {
            if (id <= 0)
            {
                throw new ArgumentOutOfRangeException("id", "Client ID out of range");
            }

            if (Toggly.HasInternetAccess())
            {
                var auth = ServiceContainer.Resolve<ITogglAuth>();

                using (var client = auth.GetPreauthenticatedClient())
                {
                    var result = await client.DeleteAsync("clients/" + id);

                    try
                    {
                        result.EnsureSuccessStatusCode();

                        await CacheLayer.RemoveEntity<Client>(id, withLock);

                        return true;
                    }
                    catch (HttpRequestException e)
                    {
                        return false;
                    }
                }
            }
            else
            {
                var existing = CacheLayer.LoadEntity<Client>(id);

                if (existing == null)
                {
                    return true;
                }

                await CacheLayer.StoreEntryWrite(existing, WriteType.Delete, withLock);
                return true;
            }
        }
    }
}